package Controls;

import Forms.AddLexeme;
import Forms.Editor;
import Forms.LexemePage;
import Core.Control;
import Core.FGSImage;
import Core.Form;
import Core.GM;
import Core.GUICanvas;
import Core.Key;
import MathCore.*;
import MathCore.Operands.*;
import MathCore.Operators.*;
import MathCore.Renders.*;
import java.util.Enumeration;
import java.util.Vector;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

/**
 *
 * @author Александр
 */
public class RenderInput extends Control
{
	private Stripe stripe = null, activeContainer;
	private Stripe result;
	private Block activeBlock = null;
	private RaSysT rstState = new RaSysT();
	private int sx, sy;
	private FGSImage bg_a = new FGSImage(), bg_p = new FGSImage(), bg_i = new FGSImage();
	public Variable attachedVar;

	private int scroll = 0;

	public static int STYLE_SIMPLE = 0, STYLE_ONLY_NAME = 1, STYLE_NAME_AND_RESULT = 2;
	private int style;

	private static int SCROLL_STEP = 16;

	public RenderInput(int width, Variable av, int style, Form parentForm)
	{
		super(width, 0);
		
		this.style = style;
		
		attachedVar = av;

		stripe = new Stripe("Main stripe");
		activeContainer = stripe;

		sx = W/2;
		sy = 0;

		bg_a.load("/ri_a.ngs");
		bg_p.load("/ri_p.ngs");
		bg_i.load("/ri_i.ngs");
		
		activeBlock = Render.activeBlock = stripe.first();

		lockOn = true;
		selectedAction = "Edit";
		pressedAction = "Back";

		this.parentForm = parentForm;

		recalc();
		upd();
	}

	public void setStyle(int style)
	{
		this.style = style;
		recalc();
	}

	private void add(Lexeme l)
	{
		if(l instanceof Number && activeBlock != null)
		{
			if(activeBlock instanceof Atom && ((Atom)activeBlock).lex() instanceof Number)
			{
				Number num = (Number) ((Atom)activeBlock).lex();
				if(num.numbers_after_point == 0)
				{
					num.get().mul(Real.TEN);
					num.get().add(((Number)l).get());
				}
				else
				{
					Real ten = new Real(Real.TEN);
					ten.pow(num.numbers_after_point);
					((Number)l).get().mul(ten);
					num.get().add(((Number)l).get());
					num.numbers_after_point--;

					System.out.println(num);
				}

				((Atom)activeBlock).setChanged();
			}
			else add(new Atom(l));
		}
		else if(l instanceof ArOperator)
		{
			ArOperator o = (ArOperator) l;
			if(o.getType() == ArOperator.DIVISION)
			{
				Stripe s1, s2 = new Stripe();
				Fraction f = new Fraction();

				Block left = activeContainer.leftLB(activeBlock);

				if(left == null) s1 = new Stripe();
				else s1 = activeContainer.squeeze(left, activeBlock);

				if(!s1.empty()) activeContainer.replace(s1, s1, f, f);
				else add(f);

				f.set(s1, s2);

				activeContainer.setChange();
				if(s1.empty())  activeContainer = s1;
				else activeContainer = s2;
				activeBlock = null;
			}
			else if(o.getType() == ArOperator.POWER)
			{
				Stripe s1 = new Stripe();
				Power p = new Power(s1);
				add(p);
				activeBlock = null;
				activeContainer = s1;
			}
			else
			{
				add(new Atom(l));
			}
		}
		else if(l instanceof Symbol)
		{
			Symbol s = (Symbol) l;
			switch(s.getType())
			{
				case Symbol.LEFT_BRACE:
				case Symbol.RIGHT_BRACE:
				case Symbol.LEFT_LIST:
				case Symbol.RIGHT_LIST:
					add(new Brace(s));
					break;

				default:
					add(new Atom(s));
					break;
			}
		}
		else
		{
			add(new Atom(l));
		}

		upd();
		updateOwner();
	}

	private void add(Block b)
	{
		//if(activeBlock != null) activeBlock.unsetFlag(Block.FLAG_CURSOR);

		if(activeBlock == null)
		{
			activeBlock = activeContainer.addToTop(b);
			//activeContainer.dbg_trace();
		}
		else if(activeContainer.last() == activeBlock)
		{
			activeBlock = activeContainer.add(b);
		}
		else
		{
			activeBlock = activeContainer.insertAfter(b, activeBlock);
			//activeContainer.dbg_trace();
		}

		upd();
		updateOwner();
	}

	private void next()
	{
		if(activeBlock != null && activeBlock.next != null) // двигаемся дальше по полосе или заходим вглубь контейнера
		{
			activeBlock = activeBlock.next;
			if(!(activeBlock == null || activeBlock instanceof Atom))
			{
				Block b = activeBlock.first();
				while(!(b instanceof Stripe)) b = b.first();
				activeBlock = null;
				activeContainer = (Stripe) b;
			}
		}
		else if(activeBlock == null && !activeContainer.empty()) // нет активного блока (стоим в начале полосы)
		{
			activeBlock = activeContainer.first();
			if(!(activeBlock == null || activeBlock instanceof Atom))
			{
				Block b = activeBlock.first();
				while(!(b instanceof Stripe)) b = b.first();
				activeBlock = null;
				activeContainer = (Stripe) b;
			}
		}
		else // выходим из контейнера
		{
			if(activeContainer == stripe)  // некуда выходить
			{
				
				upd();
				updateOwner();
				return;
			}

			if(activeContainer.next != null) // если есть куда продолжать на текущем уровне контейнера
			{
				// то переходим на следующий элемент текущего уровня
				// Stripe.next всегда Stripe
				activeContainer = (Stripe) activeContainer.next;

				// и заходим в него
				activeBlock = activeContainer.first();

				// заходим как можно глубже
				goInside();
			}
			else // если продолжать некуда
			{
				activeBlock = activeContainer.parent; // переходим на уровень вверх
				activeContainer = (Stripe) activeBlock.parent;
			}
		}
		
		upd();
		updateOwner();
	}

	private void prev()
	{
		/*if(activeBlock != null && activeBlock.prev == null && activeContainer == stripe )
		{
			activeBlock = null;
			upd();
			return;
		}*/

		if(activeBlock != null && !(activeBlock instanceof Atom))
		{
			activeContainer = (Stripe) activeBlock.last();
			activeBlock = activeContainer.last();
		}
		else if(activeBlock != null)// && activeBlock.prev != null) // двигаемся дальше по полосе или заходим вглубь контейнера
		{
			activeBlock = activeBlock.prev;
		}
		else
		{
			if(activeContainer == stripe)  // некуда выходить
			{
				upd();
				updateOwner();
				return;
			}
			if(activeContainer.prev != null) // если есть куда продолжать на текущем уровне контейнера
			{
				// то переходим на предыдущий элемент текущего уровня
				// Stripe.prev всегда Stripe
				activeContainer = (Stripe) activeContainer.prev;

				// и заходим в него
				activeBlock = activeContainer.last();

				// заходим как можно глубже
				goInside();
			}
			else // если продолжать некуда
			{
				if(activeContainer.parent == stripe)
				{
					upd();
					updateOwner();
					return;
				}
				
				activeBlock = activeContainer.parent; // переходим на уровень вверх
				activeContainer = (Stripe) activeBlock.parent;
				activeBlock = activeBlock.prev;
			}
		}

		upd();
		updateOwner();
	}

	private void goInside()
	{
		while(true)
		{
			if(activeBlock instanceof Stripe) activeContainer = (Stripe) activeBlock;
			if(activeBlock instanceof Atom || activeBlock == null) break;

			activeBlock = activeBlock.first();
		}
	}

	public void draw(Graphics g, int x, int y)
	{
		FGSImage bg = null;

		switch(state)
		{
			case Control.ACTIVE:
				bg = bg_a;
				break;
			case Control.INACTIVE:
				bg = bg_i;
				break;
			case Control.PRESSED:
				bg = bg_p;
				break;
		}
		bg.draw(g, x, y, W, H);

		Render.useCursor = state == Control.PRESSED;
		Render.textColor = 0;

		int border = 5;
		int d = 7;
		
		GUICanvas.clipMashine.clip(g, x+border, y+border, W-2*border, H-2*border);

		if(state == Control.PRESSED)
		{
			
			//x+=border;
			y+=border;
			//W-=border*2;
			H-=border*2;

			stripe.draw(null, sx+x, sy+y, Block.TOP|Block.HCENTER);
			if(activeBlock != null)
			{
				if(activeBlock.x() + activeBlock.width() >= W-d)
				{
					sx -= (activeBlock.x() + activeBlock.width()) - (W-d);
				}
				else if(activeBlock.x() <= x+d)
				{
					sx += x - activeBlock.x() + d;
				}
			}
			else
			{
				if(activeContainer.x() >= W-d)
				{
					sx -= activeContainer.x() - (W-d);
				}
				else if(activeContainer.x() <= x+d)
				{
					sx += (x+d) - activeContainer.x();
				}
			}

			if(stripe.width() <= W-d) sx = W/2;

			GUICanvas.clipMashine.clip(g, x+border, y, W-2*border, H);
			stripe.draw(g, sx+x, sy+y, Block.TOP|Block.HCENTER);
			GUICanvas.clipMashine.removeTopClip(g);

			//W+=border*2;
			H+=border*2;
		}
		else
		{
			if(style == STYLE_ONLY_NAME || style == STYLE_NAME_AND_RESULT)
			{
				result.draw(g, x+d-scroll, y+d-2, Block.TOP|Block.LEFT);
			}
			else
			{
				stripe.draw(g, x+W/2, y+5, Block.TOP|Block.HCENTER);
			}
		}

		GUICanvas.clipMashine.removeTopClip(g);
	}

	private void clear()
	{		
		if(activeBlock == null) return;

		if(activeBlock instanceof Atom && ((Atom)activeBlock).lex() instanceof Number && ((Number)((Atom)activeBlock).lex()).editable())
		{

			Number num = (Number) ((Atom)activeBlock).lex();
			if(num.numbers_after_point == 0)
			{
				num.get().div(Real.TEN);
				num.get().floor();
				if(num.get().equalTo(Real.ZERO))
				{
					boolean removedFirst = activeBlock == activeContainer.first();
					Block removed = activeContainer.remove(activeBlock);
					if(activeContainer.empty() || removedFirst)
					{
						activeBlock = null;
						upd();
						updateOwner();
						return;
					}

					if(removed.prev != null)
					{
						activeBlock = removed.prev;
					}
				}

				((Atom)activeBlock).setChanged();
			}
			else if(num.numbers_after_point == -1)
			{
				num.numbers_after_point = 0;
				((Atom)activeBlock).setChanged();
			}
			else
			{
				num.numbers_after_point++;
                Real ten = new Real(Real.TEN);
				ten.pow((-num.numbers_after_point-1));
				num.get().mul(ten);
				num.get().floor();
				num.get().div(ten);

				((Atom)activeBlock).setChanged();
			}
		}
		else
		{
			boolean removedFirst = activeBlock == activeContainer.first();
			Block removed = activeContainer.remove(activeBlock);
			if(activeContainer.empty() || removedFirst)
			{
				activeBlock = null;
				upd();
				updateOwner();
				return;
			}

			if(removed.prev != null)
			{
				activeBlock = removed.prev;
			}
		}
		
		upd();
		updateOwner();
	}

	private void up()
	{
		if(activeContainer.up != null)
		{
			activeContainer = activeContainer.up;
			activeBlock = activeContainer.first();
			goInside();
			upd();
			updateOwner();
		}

	}

	private void down()
	{
		if(activeContainer.down != null)
		{
			activeContainer = activeContainer.down;
			activeBlock = activeContainer.first();
			goInside();
			upd();
			updateOwner();
		}
	}

	private void upd()
	{
		Render.activeStripe = activeContainer;
		Render.activeBlock = activeBlock;

		if(state == Control.PRESSED) H = stripe.height()+12;
		else
		{
			if(style != STYLE_SIMPLE) H = result.height()+12;
			else H = stripe.height()+12;
		}
		if(parentManager != null) parentManager.updateDimensions();

		rstState.process(activeContainer, activeBlock);
		
	}

	public void processKey(int key, int state)
	{
		super.processKey(key, state);

		if(state == Key.PRESSED || state == Key.REPEATED)
		{
			if(key >= '0' && key <= '9') add(new Number(new Real(key - '0')));
			else if(key == Key.PR_KEY) next();
			else if(key == Key.PL_KEY) prev();
			else if(key == Key.RS_KEY) clear();
			else if(key == Key.PU_KEY) up();
			else if(key == Key.PD_KEY) down();
			else if(key == Key.STAR_KEY)
			{
				switch(rstState.opened())
				{
					case RaSysT.SIMPLE_BRACKET:
						add(new Symbol(Symbol.LEFT_BRACE));
						break;

					case RaSysT.POINT:
						if(activeBlock instanceof Atom)
							if(((Atom)activeBlock).lex() instanceof Number)
							{
								((Number)((Atom)activeBlock).lex()).numbers_after_point = -1;
								((Atom)activeBlock).setChanged();
							}
						break;
				}
			}
			else if(key == Key.LS_KEY)
			{
				switch(rstState.closed())
				{
					case RaSysT.LIST_BRACKET:
						add(new Symbol(Symbol.RIGHT_LIST));
						break;

					case RaSysT.SIMPLE_BRACKET:
						add(new Symbol(Symbol.RIGHT_BRACE));
						break;
				}
			}
		}
		else if(key == Key.POUND_KEY && state == Key.RELEASED)
		{
			Vector pages = new Vector();
			LexemePage func = new LexemePage
			(
				new TrigFunction(TrigFunction.SIN), new TrigFunction(TrigFunction.ARCSIN), new BasicFunction(BasicFunction.SQRT),
				new TrigFunction(TrigFunction.COS), new TrigFunction(TrigFunction.ARCCOS), new BasicFunction(BasicFunction.INVERT),
				new TrigFunction(TrigFunction.TG),  new Symbol(Symbol.LEFT_LIST),          new Symbol(Symbol.RIGHT_LIST)
			);

			pages.addElement(func);

			pages.addElement(new LexemePage
			(
				new BasicFunction(BasicFunction.ABS),  new BasicFunction(BasicFunction.LOG), new BasicFunction(BasicFunction.LN),
				new BasicFunction(BasicFunction.FACT), new TrigFunction(TrigFunction.ARCTG), new TrigFunction(TrigFunction.ARCCTG),
				new Constant(Real.PI, "π"),            new Constant(Real.E, "e"),            new Constant(Real.INF, "inf")
			));

			pages.addElement(new LexemePage
			(
					new Constant(new Complex(new Real(0),new Real(1),Complex.RE_IM), "i"), new ComplexFunction(ComplexFunction.CMPE), new ComplexFunction(ComplexFunction.ARG),
					new ComplexFunction(ComplexFunction.RE), new ComplexFunction(ComplexFunction.IM), new ListFunction(ListFunction.MIN),
					new ListFunction(ListFunction.AVG), new ListFunction(ListFunction.SUM), new ListFunction(ListFunction.MAX)
			));

			LexemePage oper = new LexemePage
			(
				new Symbol(Symbol.LEFT_BRACE),    new Symbol(Symbol.COMMA),           new Symbol(Symbol.DOT),
				new Symbol(Symbol.RIGHT_BRACE),   new ArOperator(ArOperator.POWER),   new ArOperator(ArOperator.DIVISION),
				new ArOperator(ArOperator.SUBSTRACTION), new ArOperator(ArOperator.PRODUCT), new ArOperator(ArOperator.ADDITION)
			);

			pages.addElement(oper);

			if(parentForm instanceof Editor)
			{
				Processor proc = Editor.proc;
				if(proc != null && !proc.variables.isEmpty())
				{
					int added=0;
					LexemePage page = null;
					for(Enumeration e = proc.variables.elements(); e.hasMoreElements(); )
					{
						if(added == 0)
						{
							page = new LexemePage();
							pages.addElement(page);
						}
						page.lexemes[added] = (Lexeme) e.nextElement();
						added++;

						if(added==9) added = 0;
					}
				}
			}

			if(rstState.type() == RaSysT.TYPE_FUNCTION)
			{
				AddLexeme alf = new AddLexeme(pages, func);
			}
			else
			{
				AddLexeme alf = new AddLexeme(pages, oper);
			}
		}
	}


	public void press()
	{
		super.press();
		updateOwner();
		upd();
	}

	public void release()
	{
		scroll = 0;
		super.release();
		recalc();
		upd();
	}

	public void updateOwner()
	{
		if(parentForm == null) return;

		switch(rstState.opened())
		{
			case RaSysT.SIMPLE_BRACKET:
				parentForm.setWindowText(GM.PANEL_STAR, "(");
				break;

			case RaSysT.POINT:
				parentForm.setWindowText(GM.PANEL_STAR, ".");
				break;

			case RaSysT.NONE:
				parentForm.setWindowText(GM.PANEL_STAR, "");
				break;
		}

		switch(rstState.closed())
		{
			case RaSysT.LIST_BRACKET:
				parentForm.setWindowText(GM.PANEL_LEFTSOFT, "]");
				break;

			case RaSysT.SIMPLE_BRACKET:
				parentForm.setWindowText(GM.PANEL_LEFTSOFT, ")");
				break;

			case RaSysT.NONE:
				parentForm.setWindowText(GM.PANEL_LEFTSOFT, "");
				break;
		}

		switch(rstState.type())
		{
			case RaSysT.TYPE_FUNCTION:
				parentForm.setWindowText(GM.PANEL_POUND, "Functions");
				break;

			case RaSysT.TYPE_OPERATOR:
				parentForm.setWindowText(GM.PANEL_POUND, "Operator");
				break;
		}

		parentForm.setWindowText(GM.PANEL_RIGHTSOFT, "Clear");

	}

	public void onEvent(int event, Object param)
	{
		if(event == GM.UPPER_FORM_CLOSED && param instanceof AddLexeme)
		{
			AddLexeme frm = (AddLexeme) param;
			if(frm.res == null) return;

			add(frm.res.clone());
		}
	}

	public Expression getExpression()
	{
		Expression exp = new Expression(Expression.TYPE_INFIX);
		stripe.extract(exp);
		return exp;
	}

	public void recalc()
	{
		if(parentForm instanceof Editor
				&& (style == STYLE_NAME_AND_RESULT || style == STYLE_ONLY_NAME))
		{
			Processor p = Editor.proc;
			result = new Stripe();
			scroll = 0;

			if(p == null) return;
			result.add(new StringRender(attachedVar.getName()+": "));

			if(style == STYLE_NAME_AND_RESULT)
			{
				if(stripe.empty())
				{
					result.add(new StringRender("not defined"));
					return;
				}
				try
				{
					Operand o = p.calculate(getExpression());
					result.add(new Atom(o));
					attachedVar.set(o);
					p.setVariable(attachedVar);
				}
				catch (ProcessorException ex)
				{
					System.out.println(ex.getMessage());
					result.add(new StringRender(ex.getMessage()));
					p.removeVariable(attachedVar);
				}
				catch (OperatorException ex)
				{
					System.out.println(ex.getMessage());
					result.add(new StringRender(ex.getMessage()));
					p.removeVariable(attachedVar);
				}
			}
			else
			{
				result.add(stripe);
			}
		}
	}

	 public void scrollRight()
	{
		if(result == null)
		{
			System.out.println("sorry :((");
			return;
		}
		if(result.width() <= getW()-12)
		{
			scroll = 0;
			System.out.println("sorry :(");
			return;
		}
		scroll += SCROLL_STEP;
		if(scroll + getW() - 12 > result.width())
		{
			scroll = result.width() - getW() + 12;
		}
	}

	public void scrollLeft()
	{
		if(result == null) return;
		if(result.width() <= getW()-12)
		{
			scroll = 0;
			return;
		}
		scroll -= SCROLL_STEP;
		if(scroll < 0) scroll = 0;
	}
}
